Análise dos dados de Covid-19 no Brasil

Autor: Leonardo Simões


1. Introdução

Este é um projeto guiado da Digital Innovation One: "Criando modelos com Python e Machine Learning para prever a evolução do COVID-19 no Brasil". O objetivo é analisar os dados das séries temporais e usar algum modelo ARIMA para predição.

Os dados usados foram obtidos na plataforma do Kaggle: https://www.kaggle.com/datasets/sudalairajkumar/novel-corona-virus-2019-dataset


2. Preparação dos dados

Na etapa de preparação dos dados são realizadas algumas operações que precedem a análise dos dados:

  • Importe de bibliotecas
  • Carregamento dos dados em um dataframe
  • Definições de formatação
  • Separação e transformações das séries
  • Definições de funções

2.1. Imports e formatação

In [1]:
# Imports das bibliotecas mais usadas
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline
In [2]:
# Imports da biblioteca Plotly
import plotly
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
In [3]:
# Imports das bibliotecas para séries temporais
from datetime import datetime
from statsmodels.tsa.seasonal import seasonal_decompose
from statsmodels.tsa.stattools import adfuller
from pmdarima.arima import auto_arima
import joblib
In [4]:
# Definições de formatação de números decimais
pd.options.display.float_format = '{:20,.2f}'.format
np.set_printoptions(precision=2)

2.2. Definições de Funções

In [5]:
# Definição da função load_df_temporal_series()
def load_df_temporal_series(diretorio_do_arquivo):
    """
    Carrega os dados em um dataframe.
    Renomeia as colunas do dataframe.
    Remove linhas duplicadas no dataframe.
    Ordena os valores por 'Data'.
    
    Retorna um DataFrame
    
    Argumento:
    diretorio_do_arquivo -- o diretório do arquivo arquivo de csv.
    """
    
    dateparse = lambda x: datetime.strptime(x, '%m/%d/%Y')
    colunas_usadas = ['ObservationDate', 'Country/Region', 'Confirmed','Deaths','Recovered']

    df = pd.read_csv(diretorio_do_arquivo, usecols=colunas_usadas, parse_dates=['ObservationDate'], date_parser=dateparse)
    
    colunas_renomeadas = {'ObservationDate':'Data', 'Country/Region':'Pais', 'Confirmed':'Casos', 
                          'Deaths':'Mortes', 'Recovered':'Recuperados'}

    df.rename(columns=colunas_renomeadas, inplace=True)
    df.drop_duplicates(keep='first', inplace=True)
    df.sort_values(by=['Data'], inplace=True)
    
    return df
In [6]:
# Definição da função somar_agrupamento()
def somar_agrupamento(df, colunas_agrupamento, colunas_soma):
    """
    Retorna um dataframe de soma por grupo. 
    
    Argumentos:
    df -- DataFrame 
    colunas_agrupamento -- list
    colunas_soma -- list
    """
    df_agrupado = df.groupby(colunas_agrupamento, as_index=False)
    df_soma = df_agrupado[colunas_soma].sum()
    
    return df_soma
In [7]:
# Definição da função mesclar_colunas()
def mesclar_colunas(df, colunas_id, colunas_valores, nome_variavel = 'Variavel', nome_value = 'Valor'):
    """
    Retorna um dataframe mesclado. 
    
    Argumentos:
    df -- DataFrame 
    colunas_id -- list
    colunas_valores -- list
    nome_variavel -- string (Opcional - Valor padrão é 'Variavel')
    nome_value -- string (Opcional - Valor padrão é 'Valor')
    """
    df_m = pd.melt(df, id_vars=colunas_id, value_vars=colunas_valores)
    df_m.rename(columns={'variable':nome_variavel, 'value':nome_value}, inplace=True)
    
    return df_m
In [8]:
# Definição da função plotar_linha()
def plotar_linha(df, eixo_x, eixo_y='Quantidade', titulo='Titulo', categorias=None):
    """
    Plota um gráfico de linhas. 
    
    Argumentos:
    df -- DataFrame 
    eixo_x -- string 
    eixo_y  -- string (Opcional - Valor padrão é 'Quantidade')
    titulo -- string (Opcional - Valor padrão é 'Titulo')
    categorias -- string (Opcional - Valor padrão é None)
    """
    fig = px.line(df, x=eixo_x, y=eixo_y,
                  color=categorias
                  )
    fig.update_layout(title_text=titulo, title_x=0.5,
                    legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01)
    )
    fig.show()
In [9]:
# Definição da função plotar_componentes_serie()
def plotar_componentes_serie(serie):
    """
    Plota gráficos da série e de suas componentes. 
    
    Argumentos:
    serie -- Serie 
    """
    decomposicao = seasonal_decompose(serie)
    
    fig = decomposicao.plot();
    fig.set_size_inches((11, 6));
    fig.tight_layout();
    plt.show();
In [10]:
# Definição da função diferenciar_serie()
def diferenciar_serie(serie):
    """
    Retorna uma série diferenciada em um passo. 
    
    Argumentos:
    serie -- Serie 
    """
    return serie.diff().dropna()
In [11]:
# Definição da função salvar_modelo_arquivo()
def salvar_modelo_arquivo(modelo, nome_arquivo='ModeloARIMA'):
    """
    Salvar um modelo em um arquivo .pkl
    
    Argumentos:
    modelo -- model ARIMA
    nome_arquivo -- string (Opcional - Valor padrão é 'ModeloARIMA')
    """
    nome_arquivo = nome_arquivo + '.pkl'
    joblib.dump(modelo, nome_arquivo)

2.3. Carregamento dos dados

In [12]:
# Diretório do arquivo de dados
ARQUIVO = './data/covid_19_data.csv'
In [13]:
df = load_df_temporal_series(ARQUIVO)
df.head()
Out[13]:
Data Pais Casos Mortes Recuperados
0 2020-01-22 Mainland China 1.00 0.00 0.00
39 2020-01-22 Kiribati 0.00 0.00 0.00
38 2020-01-22 China 0.00 0.00 0.00
36 2020-01-22 Thailand 4.00 0.00 2.00
35 2020-01-22 Japan 2.00 0.00 0.00
In [14]:
df_soma_mundo = somar_agrupamento(df, colunas_agrupamento=['Data'], colunas_soma=['Casos','Mortes','Recuperados'])
df_soma_mundo.head()
Out[14]:
Data Casos Mortes Recuperados
0 2020-01-22 531.00 17.00 30.00
1 2020-01-23 601.00 17.00 32.00
2 2020-01-24 880.00 26.00 39.00
3 2020-01-25 1,407.00 42.00 42.00
4 2020-01-26 2,043.00 56.00 56.00
In [15]:
df_brasil = df.loc[(df['Pais'] == 'Brazil') &  (df['Data'] >= '2020-02-26')]
df_brasil.head()
Out[15]:
Data Pais Casos Mortes Recuperados
2525 2020-02-26 Brazil 1.00 0.00 0.00
2631 2020-02-27 Brazil 1.00 0.00 0.00
2742 2020-02-28 Brazil 1.00 0.00 0.00
2852 2020-02-29 Brazil 2.00 0.00 0.00
2981 2020-03-01 Brazil 2.00 0.00 0.00
In [16]:
df_soma_brasil = somar_agrupamento(df_brasil, colunas_agrupamento=['Data'], colunas_soma=['Casos','Mortes','Recuperados'])
df_soma_brasil.tail()
Out[16]:
Data Casos Mortes Recuperados
454 2021-05-25 16,194,209.00 452,031.00 14,231,991.00
455 2021-05-26 16,274,695.00 454,429.00 14,272,174.00
456 2021-05-27 16,342,162.00 456,674.00 14,455,810.00
457 2021-05-28 16,391,930.00 459,045.00 14,492,701.00
458 2021-05-29 16,471,600.00 461,057.00 14,496,224.00
In [17]:
df_melt_br = mesclar_colunas(df_soma_brasil, colunas_id=['Data'], colunas_valores=['Casos', 'Mortes', 'Recuperados'], nome_variavel='Tipo', nome_value='Quantidade')
df_melt_br.head()
Out[17]:
Data Tipo Quantidade
0 2020-02-26 Casos 1.00
1 2020-02-27 Casos 1.00
2 2020-02-28 Casos 1.00
3 2020-02-29 Casos 2.00
4 2020-03-01 Casos 2.00
In [18]:
df_melt_mundo = mesclar_colunas(df_soma_mundo, colunas_id=['Data'], colunas_valores=['Casos', 'Mortes', 'Recuperados'], nome_variavel='Tipo', nome_value='Quantidade')
df_melt_mundo.head()
Out[18]:
Data Tipo Quantidade
0 2020-01-22 Casos 531.00
1 2020-01-23 Casos 601.00
2 2020-01-24 Casos 880.00
3 2020-01-25 Casos 1,407.00
4 2020-01-26 Casos 2,043.00
In [19]:
casos_br = df_soma_brasil.set_index('Data')['Casos']
novos_casos_br = diferenciar_serie(casos_br)
novos_casos_br.head()
Out[19]:
Data
2020-02-27                   0.00
2020-02-28                   0.00
2020-02-29                   1.00
2020-03-01                   0.00
2020-03-02                   0.00
Name: Casos, dtype: float64

3. Análise dos dados

In [20]:
plotar_linha(df_melt_br, eixo_x='Data', eixo_y='Quantidade', titulo='COVID no Brasil', categorias='Tipo')
In [21]:
plotar_linha(df_melt_mundo, eixo_x='Data', eixo_y='Quantidade', titulo='COVID no Mundo', categorias='Tipo')
In [22]:
# Definindo 'Data' como index
df_soma_brasil.set_index('Data', inplace=True)
df_soma_brasil.head()
Out[22]:
Casos Mortes Recuperados
Data
2020-02-26 1.00 0.00 0.00
2020-02-27 1.00 0.00 0.00
2020-02-28 1.00 0.00 0.00
2020-02-29 2.00 0.00 0.00
2020-03-01 2.00 0.00 0.00
In [23]:
df_soma_mundo.describe()
Out[23]:
Casos Mortes Recuperados
count 494.00 494.00 494.00
mean 53,141,792.91 1,263,184.20 31,275,779.94
std 52,896,854.29 1,069,666.31 30,830,259.72
min 531.00 17.00 30.00
25% 5,432,007.75 346,490.00 2,184,394.25
50% 32,458,854.50 986,592.50 22,363,140.00
75% 100,188,266.50 2,161,489.00 55,336,204.25
max 169,951,547.00 3,533,619.00 107,140,656.00
In [24]:
df_soma_brasil.describe()
Out[24]:
Casos Mortes Recuperados
count 459.00 459.00 459.00
mean 5,781,236.47 158,223.55 5,040,690.69
std 4,912,113.21 125,737.33 4,405,665.36
min 1.00 0.00 0.00
25% 1,032,913.00 49,465.00 570,352.50
50% 5,103,408.00 150,689.00 4,526,393.00
75% 9,367,856.50 228,179.00 8,339,039.00
max 16,471,600.00 461,057.00 14,496,224.00
In [25]:
plotar_componentes_serie(df_soma_brasil['Casos'])
In [26]:
plotar_componentes_serie(df_soma_brasil['Mortes'])
In [27]:
plotar_componentes_serie(df_soma_brasil['Recuperados'])
In [28]:
plotar_componentes_serie(novos_casos_br)

4. Estimativa

Nesta etapa é treinado um modelo para prever novos casos de Covid no Brasil usandos os dados passados.

4.1.Treinamento do modelo

In [29]:
modelo = auto_arima(novos_casos_br)
modelo
Out[29]:
 ARIMA(5,1,2)(0,0,0)[0]          
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
In [30]:
modelo.summary()
Out[30]:
SARIMAX Results
Dep. Variable: y No. Observations: 458
Model: SARIMAX(5, 1, 2) Log Likelihood -4901.036
Date: Thu, 03 Nov 2022 AIC 9818.072
Time: 22:32:35 BIC 9851.069
Sample: 02-27-2020 HQIC 9831.069
- 05-29-2021
Covariance Type: opg
coef std err z P>|z| [0.025 0.975]
ar.L1 0.0505 0.044 1.145 0.252 -0.036 0.137
ar.L2 -0.5355 0.033 -16.042 0.000 -0.601 -0.470
ar.L3 -0.4052 0.032 -12.497 0.000 -0.469 -0.342
ar.L4 -0.3912 0.037 -10.622 0.000 -0.463 -0.319
ar.L5 -0.4873 0.047 -10.436 0.000 -0.579 -0.396
ma.L1 -0.9754 0.043 -22.882 0.000 -1.059 -0.892
ma.L2 0.6754 0.042 16.168 0.000 0.594 0.757
sigma2 1.2e+08 1.95e-10 6.14e+17 0.000 1.2e+08 1.2e+08
Ljung-Box (L1) (Q): 1.61 Jarque-Bera (JB): 252.89
Prob(Q): 0.20 Prob(JB): 0.00
Heteroskedasticity (H): 4.62 Skew: 0.09
Prob(H) (two-sided): 0.00 Kurtosis: 6.64


Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).
[2] Covariance matrix is singular or near-singular, with condition number 2e+34. Standard errors may be unstable.

O modelo obtido foi:

$$ARIMA(5,1,2) y_t = \sum_{i=1}^5 a_iy_{t-i} + \sum_{j=1}^2 m_j\epsilon_{t-j} + \epsilon_t$$$$ARIMA(5,1,2) y_t = 0.0505y_{t-1} - 0.5355y_{t-2} - 0.4052y_{t-3} - 0.3912y_{t-4} - 0.4873y_{t-5} -0.9754\epsilon_{t-1} + 0.6754\epsilon_{t-2} + \epsilon_t$$

4.2. Análises do modelo

In [31]:
modelo.plot_diagnostics(figsize=(13, 9));
In [32]:
novos_casos_preditos = modelo.predict(n_periods=30)
novos_casos_preditos= novos_casos_preditos.to_frame().reset_index()
novos_casos_preditos.rename(columns={'index': 'Data', 0:'Casos'}, inplace=True)
novos_casos_preditos['Tipo'] = 'Predito'
novos_casos_preditos.tail()
Out[32]:
Data Casos Tipo
25 2021-06-24 73,275.87 Predito
26 2021-06-25 66,150.47 Predito
27 2021-06-26 53,984.09 Predito
28 2021-06-27 46,192.65 Predito
29 2021-06-28 48,638.09 Predito
In [33]:
novos_casos_parcela = novos_casos_br[-200:].copy()
#novos_casos_parcela = df_soma_brasil['Casos'][-200:].copy()
novos_casos_br = diferenciar_serie(casos_br)
novos_casos_parcela = novos_casos_parcela.to_frame().reset_index()
#novos_casos_parcela.rename(columns={'index': 'Data', 0:'Casos'}, inplace=True)
novos_casos_parcela['Tipo'] = 'Real'
novos_casos_parcela.tail()
Out[33]:
Data Casos Tipo
195 2021-05-25 73,453.00 Real
196 2021-05-26 80,486.00 Real
197 2021-05-27 67,467.00 Real
198 2021-05-28 49,768.00 Real
199 2021-05-29 79,670.00 Real
In [34]:
df_novos_casos = pd.concat([novos_casos_parcela, novos_casos_preditos])
In [35]:
titulo_pred = 'Números de novos casos de Covid no Brasil (Real X Predito)'
plotar_linha(df_novos_casos, eixo_x='Data', eixo_y='Casos', titulo=titulo_pred, categorias='Tipo')

4.3. Exportar modelo para arquivo

In [36]:
salvar_modelo_arquivo(modelo, nome_arquivo='Modelo_ARIMA-Covid-Brasil')

Referências

DIO (Digital Innovation One): Criando modelos com Python e Machine Learning para prever a evolução do COVID-19 no Brasil

KAGGLE - Novel Corona Virus 2019 Dataset: https://www.kaggle.com/datasets/sudalairajkumar/novel-corona-virus-2019-dataset

TOWARDS DATA SCIENCE - Time Series forecasting using Auto ARIMA in python: https://towardsdatascience.com/time-series-forecasting-using-auto-arima-in-python-bb83e49210cd